﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2010-08-15 15:31:14Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace Outcoder.UI.Xaml
{
	public static class VisualTree
	{
		//internal static void AddLogicalChild(DependencyObject parent, object child)
		//{
		//}

		//internal static void AddLogicalChild(FrameworkElement parentFE, FrameworkContentElement parentFCE, object child)
		//{
		//}

		//public static void BringIntoView(DependencyObject current)
		//{
		//}

		//public static DependencyObject FindLogicalNode(DependencyObject logicalTreeNode, string elementName)
		//{
		//}

		//public static IEnumerable GetChildren(DependencyObject current)
		//{
		//}

		//public static IEnumerable GetChildren(FrameworkElement currentElement)
		//{
		//}

		//internal static IEnumerator GetLogicalChildren(DependencyObject current)
		//{
		//}

		public static DependencyObject GetParent(DependencyObject currentObject)
		{
			ArgumentValidator.AssertNotNull(currentObject, "currentObject");
			FrameworkElement element = currentObject as FrameworkElement;
			if (element != null)
			{
				return element.Parent;
			}
			return null;
		}

#if SILVERLIGHT
		public static TAncestor GetAncestorOrSelf<TAncestor>(this DependencyObject childElement)
			where TAncestor : class
		{
			/* Silverlight VisualTreeHelper requires a UIElement, while WPF doesn't;
			 * hence the two methods. */
			return GetAncestorOrSelfCore<TAncestor>(childElement);
		}
#else
		public static TAncestor GetAncestorOrSelf<TAncestor>(this UIElement childElement)
			where TAncestor : class
		{
			return GetAncestorOrSelfCore<TAncestor>(childElement);
		}
#endif

		static TAncestor GetAncestorOrSelfCore<TAncestor>(DependencyObject childElement)
			where TAncestor : class
		{
			ArgumentValidator.AssertNotNull(childElement, "childElement");
			DependencyObject parent = childElement;
			while (parent != null)
			{
				TAncestor result = parent as TAncestor;
				if (result != null)
				{
					return result;
				}
				parent = VisualTreeHelper.GetParent(parent);
			}
			return null;
		}

		//internal static void RemoveLogicalChild(DependencyObject parent, object child)
		//{
		//}

		//internal static void RemoveLogicalChild(FrameworkElement parent, object child)
		//{
		//}

		public static IEnumerable GetChildren(DependencyObject current)
		{
			ArgumentValidator.AssertNotNull(current, "current");
			FrameworkElement element = current as FrameworkElement;
			if (element != null)
			{
				return GetLogicalChildren(element);
			}
			return new List<DependencyObject>();
		}

 

		static int nameCounter;

		/// <summary>
		/// Retrieves the logical children of a <see cref="FrameworkElement"/>.  
		/// A visual element is assumed to be a logical 
		/// child of another visual element if they are in the same namescope.
		/// Retrieved from http://themechanicalbride.blogspot.com/2008/10/silverlight-and-logical-tree.html
		/// </summary>
		/// <param name="parent">The parent element.</param>
		/// <returns>The logical children of the specified <see cref="FrameworkElement"/>.</returns>
		internal static IEnumerable<FrameworkElement> GetLogicalChildren(this FrameworkElement parent)
		{
			ArgumentValidator.AssertNotNull(parent, "parent");

			/* This should be rewritten to avoid naming elements. */
			if (string.IsNullOrEmpty(parent.Name))
			{
				parent.Name = "generatedName_" + nameCounter++;
			}

			string parentName = parent.Name;
			var children = parent.GetVisualChildren().OfType<FrameworkElement>();
			Stack<FrameworkElement> stack = new Stack<FrameworkElement>(children);

			while (stack.Count > 0)
			{
				FrameworkElement element = stack.Pop();
				if (element.FindName(parentName) == parent)
				{
					yield return element;
				}
				else
				{
					foreach (FrameworkElement visualChild in element.GetVisualChildren().OfType<FrameworkElement>())
					{
						stack.Push(visualChild);
					}
				}
			}
		}

		internal static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject parent)
		{
			ArgumentValidator.AssertNotNull(parent, "parent");

			int childCount = VisualTreeHelper.GetChildrenCount(parent);
			for (int counter = 0; counter < childCount; counter++)
			{
				yield return VisualTreeHelper.GetChild(parent, counter);
			}
		}

		/// <summary>
		/// Tries the get ancestor of the specified FrameworkElement 
		/// with the specified generic type. Walks the visual tree.
		/// </summary>
		/// <typeparam name="TAncestor">The type of the ancestor.</typeparam>
		/// <param name="frameworkElement">The child framework element.</param>
		/// <param name="ancestor">The resulting ancestor.</param>
		/// <returns>The <c>true</c> if an ancestor was found of the specified type; 
		/// <c>false</c> otherwise.</returns>
		public static bool TryGetAncestorOrSelf<TAncestor>(
			this FrameworkElement frameworkElement, out TAncestor ancestor)
			where TAncestor : class
		{
			ArgumentValidator.AssertNotNull(frameworkElement, "frameworkElement");
			TAncestor result = null;
			GetAncestorOrSelf(frameworkElement, ref result);
			ancestor = result;
			return ancestor != null;
		}

		static void GetAncestorOrSelf<TAncestor>(
			FrameworkElement frameworkElement, ref TAncestor result)
			where TAncestor : class
		{
			if (frameworkElement == null) /* Terminal condition. */
			{
				return; /* Terminal case. */
			}

			var castedElement = frameworkElement as TAncestor;
			if (castedElement != null) /* Terminal condition. */
			{
				result = castedElement; /* Terminal case. */
				return;
			}

			FrameworkElement parent = VisualTreeHelper.GetParent(frameworkElement) as FrameworkElement;
			GetAncestorOrSelf(parent, ref result); /* Tail recursive. */
		}

		/// <summary>
		/// Gets the visual parent of the specified element.
		/// </summary>
		/// <param name="childElement">The child element.</param>
		/// <returns>The visual parent</returns>
		public static FrameworkElement GetVisualParent(
			this FrameworkElement childElement)
		{
			return VisualTreeHelper.GetParent(childElement) as FrameworkElement;
		}

		/// <summary>
		/// Gets the ancestors of the element.
		/// </summary>
		/// <param name="descendentElement">The start element.</param>
		/// <returns>The ancestors of the specified element.</returns>
		public static IEnumerable<FrameworkElement> GetVisualAncestors(
			this FrameworkElement descendentElement)
		{
			ArgumentValidator.AssertNotNull(descendentElement, "descendentElement");
			FrameworkElement parent = descendentElement.GetVisualParent();
			while (parent != null)
			{
				yield return parent;
				parent = parent.GetVisualParent();
			}
		}

		/// <summary>
		/// Gets the ancestors of the element.
		/// </summary>
		/// <param name="descendentElement">The start element.</param>
		/// <returns>The ancestors of the specified element.</returns>
		public static IEnumerable<TAncestor> GetVisualAncestors<TAncestor>(
			this FrameworkElement descendentElement) where TAncestor : class
		{
			ArgumentValidator.AssertNotNull(descendentElement, "descendentElement");
			FrameworkElement parent = descendentElement.GetVisualParent();
			while (parent != null)
			{
				TAncestor item = parent as TAncestor;
				if (item != null)
				{
					yield return item;
				}
				parent = parent.GetVisualParent();
			}
		}

		/// <summary>
		/// Gets the ancestors of the object of the specified type in an order beginning with the parent,
		/// and working up the visual tree.
		/// </summary>
		public static IEnumerable<T> GetVisualAncestors<T>(this DependencyObject startObject, Predicate<T> predicate) 
			where T : DependencyObject
		{
			for ( ; ; )
			{
				DependencyObject parent = VisualTreeHelper.GetParent(startObject);
				if (parent == null)
				{
					break;
				}

				T parentAsT = parent as T;
				if (predicate == null || predicate(parentAsT))
				{
					yield return parentAsT;
				}

				startObject = parent;
			}
		}
	}
}
